﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Globalization;

using Microsoft.Ccr.Core;
using Microsoft.Dss.Core;
using Microsoft.Dss.Core.Attributes;
using Microsoft.Dss.ServiceModel.Dssp;
using Microsoft.Dss.ServiceModel.DsspServiceBase;

using Microsoft.Robotics.Simulation;
using Microsoft.Robotics.Simulation.Engine;
using engineproxy = Microsoft.Robotics.Simulation.Engine.Proxy;
using Microsoft.Robotics.Simulation.Physics;
using Microsoft.Robotics.PhysicalModel;
using System.ComponentModel;

namespace worldParser
{
    struct Float
    {
        static public float Parse(string s)
        {
            return Single.Parse(s, CultureInfo.InvariantCulture);
        }
    }

    public interface IWorldStackObject { };

    public abstract class CWorldObject : IWorldStackObject
    {
        private bool _reused = false;
        public bool Reused
        {
            get { return _reused; }
            set { _reused = value; }
        }

        public virtual void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            throw new Exception(String.Format("Unhandled set parameter {0}. {1}", ToString(), param));
        }

        public virtual void PostParamByName(string param, IWorldStackObject value)
        {
            throw new Exception(String.Format("Unhandled post parameter {0}. {1}", ToString(), param));
        }

        public virtual void Setup()
        {            
        }
    }

    public class Point2D
    {
        public float x, y;

        public Point2D(List<string> list)
        {
            x = Float.Parse(list[0]);
            y = Float.Parse(list[1]);
        }

        public Point2D(float _x, float _y)
        {
            x = _x;
            y = _y;
        }

        public override string ToString()
        {
            return x.ToString() + " "+ y.ToString();
        }

        public Vector2 ToVector2()
        {
            return new Vector2(x, y);
        }
    }

    public class Point3D : Point2D
    {
        public float z;

        public Point3D(List<string> list)   : base(list)
        {
            z = Float.Parse(list[2]);
        }

        public Point3D(float _x, float _y, float _z) : base (_x,_y)
        {
            z = _z;
        }

        public override string ToString()
        {
            return base.ToString()+" "+z.ToString();
        }

        public Vector3 ToVector3()
        {
            return new Vector3(base.x, base.y, z);            
        }
    }

    public class Rotation3D : Point3D
    {
        public float alpha;

        public Rotation3D(List<string> list) : base(list)
        {
            alpha = Float.Parse(list[3]);
        }

        public Rotation3D(float _x, float _y, float _z, float _alpha)
            : base(_x,_y,_z)
        {
            alpha = _alpha;
        }

        public Vector4 ToVector4()
        {
            return new Vector4(base.x, base.y, base.z, alpha);
        }
    }

    public class RGBColor
    {
        float r, g, b;

        public RGBColor(List<string> list)
        {
            r = Float.Parse(list[0]);
            g = Float.Parse(list[1]);
            b = Float.Parse(list[2]);
        }
    }

    public class CWorldArray : ArrayList, IWorldStackObject
    {
    }

    public class Vec3f : CWorldArray
    {
    }

    public class CUnknown : CWorldObject
    {
        public CUnknown(string s)
        {
            throw new NotSupportedException("Unknown class "+s);
        }
    }

    public class CWorldInfo : CWorldObject
    {
        CWorldArray info;
        string title, physics, fast2d;
        Point3D gravity = new Point3D(0f, -9.81f, 0f);
        float CFM, ERP = 0.2f, basicTimeStep = 32, inkEvaporation;
        int displayRefresh = 2;
        bool runRealTime;

        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            switch (param)
            {
                case "info": info = new CWorldArray();
                      objstack.Push(info); break;
                case "title": title = values[0]; break;
                case "physics": physics = values[0]; break;
                case "fast2d": fast2d = values[0]; break;
                case "gravity": gravity = new Point3D(values); break;
                case "CFM": CFM = Float.Parse(values[0]); break;
                case "ERP": ERP = Float.Parse(values[0]); break;
                case "basicTimeStep": basicTimeStep = Float.Parse(values[0]); break;
                case "inkEvaporation": inkEvaporation = Float.Parse(values[0]); break;
                case "displayRefresh": displayRefresh = int.Parse(values[0]); break;
                case "runRealTime": runRealTime = values[0][0] == 'T'; break;

                default: base.SetParamByName(objstack, param, values); break;
            }
        }
    }

    public class CViewpoint : CWorldObject
    {
        float fieldOfView = 0.785398f, far = 50.0f, near = 0.05f;
        Rotation3D orientation;
        Point3D position;
        string description;

        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            switch (param)
            {
                case "fieldOfView": fieldOfView = Float.Parse(values[0]); break;
                case "far": far = Float.Parse(values[0]); break;
                case "near": near = Float.Parse(values[0]); break;
                case "orientation": orientation = new Rotation3D(values); break;
                case "position": position = new Point3D(values); break;
                case "description": description = values[0]; break;

                default: base.SetParamByName(objstack, param, values); break;
            }
        }

        public override void Setup()
        {
            CameraView view = new CameraView();
            view.EyePosition = position.ToVector3();
            view.LookAtPoint = orientation.ToVector3();
            SimulationEngine.GlobalInstancePort.Update(view);
        }
    }

    public class CBackground : CWorldObject
    {
        CWorldArray skyColor;
        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            switch (param)
            {
                case "skyColor": skyColor = new CWorldArray();
                    objstack.Push(skyColor); break;
                default: base.SetParamByName(objstack, param, values); break;
            }
        }
    }

    public class CLight : CWorldObject
    {
        float  ambientIntensity,intensity = 1.0f;
        RGBColor color;
        bool on = true, castShadows = true; 

        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            switch (param)
            {
                case "ambientIntensity": ambientIntensity = Float.Parse(values[0]); break;
                case "intensity": intensity = Float.Parse(values[0]); break;
                case "on": on = values[0][0] == 'T'; break;
                case "color": color = new RGBColor(values); break;
                case "castShadows": castShadows = values[0][0] == 'T'; break;

                default: base.SetParamByName(objstack, param, values); break;
            }
        }
    }

    public class CPointLight : CLight
    {        
        Point3D attenuation, location;
        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            switch (param)
            {               
                case "attenuation": attenuation = new Point3D(values); break;
                case "location": location = new Point3D(values); break;

                default: base.SetParamByName(objstack, param, values); break;
            }
        }
    }

    public class CShape : CWorldObject
    {
        CAppearance appearance;
        CGeometry geometry;

        public override void PostParamByName(string param, IWorldStackObject value)
        {      
            switch (param)
            {
                case "appearance": appearance = (CAppearance) value; break;
                case "geometry": geometry = (CGeometry) value; break;

                default: base.PostParamByName(param, value); break;
            }
        }
    }

    public class CPhysics : CWorldObject
    {
        float density, mass, bounce, bounceVelocity, coulombFriction, forceDependentSlip;
        CWorldArray inertiaMatrix;
        Point3D centerOfMass;
        Rotation3D orientation;

        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            switch (param)
            {
                case "density": density = Float.Parse(values[0]); break;
                case "mass": mass = Float.Parse(values[0]); break;
                case "bounce": bounce = Float.Parse(values[0]); break;
                case "bounceVelocity": bounceVelocity = Float.Parse(values[0]); break;
                case "coulombFriction": coulombFriction = Float.Parse(values[0]); break;
                case "forceDependentSlip": forceDependentSlip = Float.Parse(values[0]); break;
                case "inertiaMatrix": inertiaMatrix = new CWorldArray();
                    objstack.Push(inertiaMatrix); break;
                case "centerOfMass": centerOfMass = new Point3D(values); break;
                case "orientation": orientation = new Rotation3D(values); break;

                default: base.SetParamByName(objstack, param, values); break;
            }
        }
    }

    public class CMaterial : CWorldObject
    {
        float ambientIntensity, shininess, transparency;
        RGBColor diffuseColor, emissiveColor, specularColor;

        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            switch (param)
            {
                case "ambientIntensity": ambientIntensity = Float.Parse(values[0]); break;
                case "shininess": shininess = Float.Parse(values[0]); break;
                case "transparency": transparency = Float.Parse(values[0]); break;
                case "diffuseColor": diffuseColor = new RGBColor(values); break;
                case "emissiveColor": emissiveColor = new RGBColor(values); break;
                case "specularColor": specularColor = new RGBColor(values); break;

                default: base.SetParamByName(objstack, param, values); break;
            }
        }
    }

    public class CImageTexture : CWorldObject
    {
        CWorldArray url;
        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
          switch (param)
          {
              case "url": 
                  url = new CWorldArray();
                    objstack.Push(url);
                    break;
              default: base.SetParamByName(objstack, param, values); break;
          }
        }
    }

    public class CAppearance : CWorldObject
    {
        CMaterial material;
        CImageTexture texture;

        public override void PostParamByName(string param, IWorldStackObject value)
        {
            switch (param)
            {
                case "material": material = (CMaterial)value; break;
                case "texture": texture = (CImageTexture)value; break;
                default: base.PostParamByName(param, value); break;
            }
        }
    }

    public class CCoordinate : CWorldObject
    {
        CWorldArray point;

        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            switch (param)
            {
                case "point": point = new CWorldArray();
                    objstack.Push(point); break;

                default: base.SetParamByName(objstack, param, values); break;
            }
        }
    }

    public class CTextureCoordinate : CWorldObject
    {
        CWorldArray point;

        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            switch (param)
            {
                case "point": point = new CWorldArray();
                    objstack.Push(point); break;

                default: base.SetParamByName(objstack, param, values); break;
            }
        }
    }

    public class CColors : CWorldObject
    {
        CWorldArray color;
        public override void SetParamByName(Stack<IWorldStackObject> objstack, string param, List<string> values)
        {
            switch (param)
            {
                case "color": color = new CWorldArray();
                    objstack.Push(color); break;

                default: base.SetParamByName(objstack, param, values); break;
            }
        }
    }
}